package com.region.http.client.model;

import com.region.http.client.common.ContentType;
import com.region.http.client.common.RequestType;
import com.region.http.client.config.RequestAnnotationConfig;
import com.region.http.client.config.RequestAnnotationConfig.RequestAnnotationConfigBuilder;
import com.region.http.client.model.ProtectorMeta.ProtectorMetaBuilder;
import com.region.http.client.protector.RequestProtector;
import com.region.http.client.request.HttpExecutor;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * The Region meta information of request interface
 *
 * @author liujieyu
 * @date 2023/6/4 22:55
 * @desciption
 */
public class RequestMeta {

    private String originUrl;

    private String url;

    private RequestAnnotationConfig requestAnnotationConfig;

    private Method method;

    private Class<? extends HttpExecutor> executor;

    /**
     * The return class and the generic type of class
     */
    private Type returnType;

    /**
     * The meta info of parameter index
     */
    private MetaParameter metaParameter;

    /**
     * The constant parameter
     */
    private ConstParameter constParameter;

    /**
     * Is it a multiple instance
     * true/multi-instance, false/single-instance
     */
    private boolean status;

    /**
     * the server group name of multi-instance
     */
    private String groupName;

    /**
     * The current object environment variable information.
     */
    private List<String> envList;

    /**
     * Asynchronous or not
     */
    private boolean asynchronous;

    /**
     * The protector meta info
     */
    private ProtectorMeta protectorMeta;

    /**
     * the custom extension request meta info
     */
    private CustomRequestMeta customRequestMeta;

    public RequestMeta(String originUrl, String url, boolean status, RequestAnnotationConfig requestAnnotationConfig,
                       Method method, Class<? extends HttpExecutor> executor, Type returnType, MetaParameter metaParameter,
                       ConstParameter constParameter, boolean asynchronous, ProtectorMeta protectorMeta) {
        this.originUrl = originUrl;
        this.url = url;
        this.status = status;
        this.requestAnnotationConfig = requestAnnotationConfig;
        this.method = method;
        this.executor = executor;
        this.returnType = returnType;
        this.metaParameter = metaParameter;
        this.constParameter = constParameter;
        this.asynchronous = asynchronous;
        this.protectorMeta = protectorMeta;
        this.customRequestMeta = new CustomRequestMeta(originUrl, method, status, groupName);
    }

    public String getOriginUrl() {
        return originUrl;
    }

    public String getUrl() {
        return url;
    }

    public boolean isStatus() {
        return this.customRequestMeta.isExtension() ? this.customRequestMeta.isStatus() : this.status;
    }

    public RequestAnnotationConfig getRequestAnnotationConfig() {
        return requestAnnotationConfig;
    }

    public Method getMethod() {
        return method;
    }

    public Class<? extends HttpExecutor> getExecutor() {
        return executor;
    }

    public Type getReturnType() {
        return returnType;
    }

    public MetaParameter getMetaParameter() {
        return metaParameter;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setStatus(boolean status) {
        this.status = status;
        if (!this.customRequestMeta.isExtension()) {
            this.customRequestMeta.setNoExtensionStatus(status);
        }
    }

    public void setExecutor(Class<? extends HttpExecutor> executor) {
        this.executor = executor;
    }

    public String getGroupName() {
        return this.customRequestMeta.isExtension() ? customRequestMeta.getGroupName() : groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
        if (!this.customRequestMeta.isExtension()) {
            this.customRequestMeta.setNoExtensionGroupName(groupName);
        }
    }

    public List<String> getEnvList() {
        return envList;
    }

    public void setEnvList(List<String> envList) {
        this.envList = envList;
    }

    public ConstParameter getConstParameter() {
        return constParameter;
    }

    public boolean isAsynchronous() {
        return asynchronous;
    }

    public ProtectorMeta getProtectorMeta() {
        return protectorMeta;
    }

    public CustomRequestMeta getCustomRequestMeta() {
        return customRequestMeta;
    }

    public static RequestMetaBuilder builder() {
        return new RequestMetaBuilder();
    }

    public static class MetaParameter {

        private Map<String, Integer> pathIndex;

        private Map<String, Integer> keyIndex;

        private Map<String, Integer> headerIndex;

        private Integer bodyIndex;

        private Integer callableIndex;

        private MetaParameter(Map<String, Integer> pathIndex, Map<String, Integer> keyIndex,
                              Map<String, Integer> headerIndex, Integer bodyIndex, Integer callableIndex) {
            this.pathIndex = pathIndex;
            this.keyIndex = keyIndex;
            this.headerIndex = headerIndex;
            this.bodyIndex = bodyIndex;
            this.callableIndex = callableIndex;
        }

        public Map<String, Integer> getPathIndex() {
            return pathIndex;
        }

        public Map<String, Integer> getKeyIndex() {
            return keyIndex;
        }

        public Map<String, Integer> getHeaderIndex() {
            return headerIndex;
        }

        public Integer getBodyIndex() {
            return bodyIndex;
        }

        public Integer getCallableIndex() {
            return callableIndex;
        }

        public static MetaParameterBuilder builder() {
            return new MetaParameterBuilder();
        }

        public static final class MetaParameterBuilder {

            private Map<String, Integer> pathIndex;

            private Map<String, Integer> keyIndex;

            private Map<String, Integer> headerIndex;

            private Integer bodyIndex;

            private Integer callableIndex;

            private MetaParameterBuilder() {
                super();
            }

            public MetaParameterBuilder pathIndex(String name, Integer index) {
                if (this.pathIndex == null) {
                    this.pathIndex = new HashMap<>();
                }
                this.pathIndex.put(name, index);
                return this;
            }

            public MetaParameterBuilder keyIndex(String name, Integer index) {
                if (this.keyIndex == null) {
                    this.keyIndex = new HashMap<>();
                }
                this.keyIndex.put(name, index);
                return this;
            }

            public MetaParameterBuilder headerIndex(String name, Integer index) {
                if (this.headerIndex == null) {
                    this.headerIndex = new HashMap<>();
                }
                this.headerIndex.put(name, index);
                return this;
            }

            public MetaParameterBuilder bodyIndex(Integer bodyIndex) {
                this.bodyIndex = bodyIndex;
                return this;
            }

            public MetaParameterBuilder callable(Integer callableIndex) {
                this.callableIndex = callableIndex;
                return this;
            }

            public MetaParameter build() {
                return new MetaParameter(pathIndex, keyIndex, headerIndex, bodyIndex, callableIndex);
            }
        }
    }

    public static class ConstParameter {

        private Map<String, String> constPath;

        private Map<String, String> constHeader;

        private Map<String, String> constParameter;

        private String constBody;

        private ConstParameter(Map<String, String> constPath, Map<String, String> constHeader, Map<String, String> constParameter, String constBody) {
            this.constPath = constPath;
            this.constHeader = constHeader;
            this.constParameter = constParameter;
            this.constBody = constBody;
        }

        public Map<String, String> getConstPath() {
            return constPath;
        }

        public Map<String, String> getConstHeader() {
            return constHeader;
        }

        public Map<String, String> getConstParameter() {
            return constParameter;
        }

        public String getConstBody() {
            return constBody;
        }

        public static ConstParameterBuilder builder() {
            return new ConstParameterBuilder();
        }

        public static final class ConstParameterBuilder {

            private Map<String, String> constPath;

            private Map<String, String> constHeader;

            private Map<String, String> constParameter;

            private String constBody;

            public ConstParameterBuilder addPathConst(String key, String value) {
                if (constPath == null) {
                    constPath = new HashMap<>();
                }
                constPath.putIfAbsent(key, value);
                return this;
            }

            public ConstParameterBuilder addHeaderConst(String key, String value) {
                if (constHeader == null) {
                    constHeader = new HashMap<>();
                }
                constHeader.putIfAbsent(key, value);
                return this;
            }

            public ConstParameterBuilder addParameterConst(String key, String value) {
                if (constParameter == null) {
                    constParameter = new HashMap<>();
                }
                constParameter.putIfAbsent(key, value);
                return this;
            }

            public ConstParameterBuilder addBodyConst(String body) {
                if (this.constBody == null) {
                    this.constBody = body;
                }
                return this;
            }

            public ConstParameter build() {
                return new ConstParameter(constPath, constHeader, constParameter, constBody);
            }
        }

    }

    public static final class RequestMetaBuilder {

        private String originUrl;

        private String url;

        private boolean status;

        private RequestAnnotationConfigBuilder builder = RequestAnnotationConfig.builder();

        private Method method;

        private Class<? extends HttpExecutor> executor;

        private Type returnType;

        private MetaParameter metaParameter;

        private ConstParameter constParameter;

        private boolean asynchronous = false;

        private ProtectorMetaBuilder protectorMetaBuilder;

        private RequestMetaBuilder() {
        }

        public RequestMetaBuilder originUrl(String originUrl) {
            this.originUrl = originUrl;
            return this;
        }

        public String originUrl() {
            return this.originUrl;
        }

        public RequestMetaBuilder url(String url) {
            this.url = url;
            return this;
        }

        public RequestMetaBuilder status(boolean status) {
            this.status = status;
            return this;
        }

        public RequestMetaBuilder requestType(RequestType requestType) {
            builder.requestType(requestType);
            return this;
        }

        public RequestMetaBuilder contentType(ContentType contentType) {
            if (contentType != ContentType.NULL) {
                builder.contentType(contentType);
            }
            return this;
        }

        public RequestMetaBuilder connectTime(int connectTime) {
            if (connectTime > 0) {
                builder.connectTime(connectTime);
            }
            return this;
        }

        public RequestMetaBuilder readTime(int readTime) {
            if (readTime > 0) {
                builder.readTime(readTime);
            }
            return this;
        }

        public RequestMetaBuilder requestTime(int requestTime) {
            if (requestTime > 0) {
                builder.requestTime(requestTime);
            }
            return this;
        }

        public RequestMetaBuilder method(Method method) {
            this.method = method;
            return this;
        }

        public RequestMetaBuilder executor(Class<? extends HttpExecutor> executor) {
            if (HttpExecutor.class != executor && HttpExecutor.class.isAssignableFrom(executor)) {
                this.executor = executor;
            }
            return this;
        }

        public Class<? extends HttpExecutor> executor() {
            return this.executor;
        }

        public RequestMetaBuilder type(Type type) {
            this.returnType = type;
            return this;
        }

        public RequestMetaBuilder metaParameter(MetaParameter metaParameter) {
            this.metaParameter = metaParameter;
            return this;
        }

        public RequestMetaBuilder constParameter(ConstParameter constParameter) {
            this.constParameter = constParameter;
            return this;
        }

        public RequestMetaBuilder asynchronous() {
            this.asynchronous = true;
            return this;
        }

        public RequestMetaBuilder intervalTime(int intervalTime) {
            if (intervalTime > 0) {
                if (protectorMetaBuilder == null) {
                    protectorMetaBuilder = ProtectorMeta.builder();
                }
                protectorMetaBuilder.intervalTime(intervalTime);
            }
            return this;
        }

        public RequestMetaBuilder exceedsTime(int exceedsTime) {
            if (exceedsTime > 0) {
                if (protectorMetaBuilder == null) {
                    protectorMetaBuilder = ProtectorMeta.builder();
                }
                protectorMetaBuilder.exceedsTime(exceedsTime);
            }
            return this;
        }

        public RequestMetaBuilder protector(Class<? extends RequestProtector> protectorClazz) {
            if (protectorClazz != null) {
                if (protectorMetaBuilder == null) {
                    protectorMetaBuilder = ProtectorMeta.builder();
                }
                protectorMetaBuilder.protector(protectorClazz);
            }
            return this;
        }

        public RequestMeta build() {
            return new RequestMeta(originUrl, url, status, builder.build(), method, executor, returnType,
                    metaParameter, constParameter, asynchronous, protectorMetaBuilder == null ? null : protectorMetaBuilder.build());
        }
    }

}
